'use strict'

const http = require('http')
// const { buffer } = require('stream/consumers')
const fs  = require('fs')
const fsp = fs.promises

//解析文件扩展名
function parseExtName(filename){
    if(filename.length == 2){
        return ''
    }

    let nsplit = filename.split(' . ').filter(p => p.length > 0)
    if(nsplit.length > 2) return ''
    return `.${nsplit[ nsplit.length - 1]}`
}
/**------------------------------解析body数据------------------------------*/

/**
 * Content-Disposition: form-data; name="file"; filename="xxx"
 * Content-Type: text/plain
 * @param {string} headerData
 * 
 */
function parseBodyHeaders(headerData) {
    let hlist = headerData.split('\r\n').filter(p => p.length >0)

    //解析content-disposition => hlist[0]
    //按照 ； 切分数组，选出从第二个元素开始的数组
    let csplit = hlist[0].split('; ').slice(1)

    let filename
    let name
    for(let a of csplit) {
        if(a.indexOf('filename=') === 0){
            filename =  a.substring(a.indexOf('=')+2, a.length-1)
        } else {
            name = a.substring(a.indexOf('=')+2, a.length-1)
        }
    }


    let headers = {}
    let ind = 0
    let k
    for (let h of hlist){
        ind = h.indexOf(':')
        k = h.substring(0, ind).toLowerCase
        headers[k] = h.substring(ind+1).trim()
    }
    return {
        filename,
        name,
        headers,
        contentType: headers['content-type'] || ''
    }

}

function parseBody(bodyData, headers){
    let ctype = headers['content-type']
    let bdy = ctype.substring(ctype.indexOf('=') + 1)
    let crlf_bdy = `\f\n--${bdy}`
    let header_end_index = bodyData.indexOf('\r\n\r\n', crlf_bdy.length);
    let headerData = bodyData.toString('utf-8',crlf_bdy.length, header_end_index);

    //解析头文件信息
    let hd = parseBodyHeaders(headerData)
    let file = {
        start:header_end_index + 4,
        end:bodyData.indexOf(crlf_bdy, header_end_index),
        ...hd
    }
    file.length = file.end - file.start
    return file
}
/**--------------------------解析Body数据 -- END ------------------------- */

let html = `
    <!DOCTYPE html>
    <html>
        <head>
            <title>First HTTP</title>
        </head>
        <body>
            你好
        </body>
    <html> `
    let routerTable ={
    GET: {
        '/':async(ctx)=>{
            ctx.response.setHeader('content-type', 'text/html; charset=utf-8')
            ctx.end(html)

        },
        '/test':async(ctx)=>{
            let status =parseInt(Math.random() * 200) +199
            ctx.response.statusCode = status
            ctx.end('test end')
        },
        '/upload':async(ctx)=>{
            let stm = fs.createReadStream('./upload.html')
            stm.pipe(ctx.response,{end:false})
            stm.on('end',()=>{
                ctx.end()
            })
        }
    },

    POST :{
        '/data':async(ctx)=>{
            let file = parseBody(ctx.rawBody, ctx.headers)
            console.log(file)

            if(!ctx.rawBody){
                ctx.end();
                return;
            }
            let fname = `${Date.now()}-${Math.random()}` + `${parseExtName(file.filename)}`
            let fh = await fsp.open(fname, 'w+')
            let fret = await fh.write(ctx.rawBody, file.start, file.length, 0)
            //关闭打开的文件
            fh.close()
            ctx.end(`${fret.bytesWritten}`)
            //ctx.end(ctx.rawBody)

            // let bufferLength=0
            // let bufferList =[]
            // request.on('data',chunk => {
            //     bufferLength += chunk.length
            //     bufferList.push(chunk)
            // })
            
            // let bodyData = null
            // request.on('end',()=>{
            //     if(bufferList.length>0){
            //         bodyData = Buffer.concat(bufferList,bufferLength)
            //         bufferList=[]
            //     }

            //     let file = parseBody(bodyData,request.headers)
 
            //     //parseBody(bodyData, request.headers)

            //     if(bodyData) {
            //         response.end(bodyData)
            //     }else{
            //         response.end()
            //     }
        
            // })
        }
    }
}
http.createServer(async (request, response) => {

    let rm = routerTable[request.method]
    let usplit = request.url.split('?')
    let pathname = usplit[0]
    let querystring =usplit[1]

    if(!rm || !rm[ pathname ]){
        response.statusCode = 404
        response.end('page not found')
        return
    }
    //ctx 是 context的缩写，意为请求上下文，就是打包了各种数据和方法的对象
    let ctx = {
        //各种请求
        //GET   HEAD   TRACE  OPTIONS
        //POST  PUT    PATCH
        //DELETE
        method: request.method,
        path:pathname,
        search:querystring,
        headers:request.headers,
        request:request,
        response:response,
        end:(data, encoding = 'utf8') =>{
            response.end(data, encoding)
        },
        write: (data, encoding='utf8') =>{
            response.write(data, encoding)
        },
        totalLength:0,
        rawBody:null,
        bufferList:[]
    }
    await new Promise( (rv, rj)=>{
        if('PD'.indexOf(request.method[0]) >=0){
            request.on('data', chunk =>{
                ctx.bufferList.push(chunk)
                ctx.totalLength += chunk.length
            })
        } else {
            request.on('data', data =>{
                response.statusCode = 400
                response.end('bad request', ()=>{  
                    request.destroy()
                })
            })
        }
        let resolved = false;
        request.on('end', ()=>{
            resolved = true;
            rv();
        })
        request.on('close', ()=>{
            (!resolved) && rv();
        })
    })
    if(!response.write || response.writableEnded){
        return
    }
    if(ctx.bufferList.length > 0){
        ctx.rawBody = Buffer.concat(ctx.bufferList, ctx.totalLength)
        ctx.bufferList = []
    }
    rm[ pathname ](ctx)
}).listen(3456)  //3456是端口，端口是受保护的消息队列

/***
 * .jpg .jpeg  --> image/jpeg
 * .png -->image/png
 * .gif ---> image/gif
 * .ico ===>image/x-icon
 * .txt ->text/plain
 * .json ->text/json 或application/json
 * .xml  -->text/xml 或 application/xml
 */